package com.zz.framework.core.utils.file

import android.content.ContentUris
import android.content.ContentValues
import android.content.Context
import android.graphics.Bitmap
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import android.util.Log
import androidx.annotation.RequiresApi
import com.zz.framework.core.utils.file.sandbox.FileType
import java.io.BufferedInputStream
import java.io.BufferedOutputStream
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream

/**
 * Copyright © 2024 zhun All rights reserved.
 * Created by Zha0Zhun on 2024/6/20 22:56.
 * @author: Zha0Zhun 1820022519@qq.com
 * @version: V1.0
 */
class StorageRepository(private val context: Context) {
    fun saveFile(
        displayName: String,
        content: ByteArray,
        onSuccess: (Uri) -> Unit,
        onError: (Exception) -> Unit
    ) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            saveFileScopedStorage(displayName, content, onSuccess, onError)
        } else {
            saveFileLegacy(displayName, content, onSuccess, onError)
        }
    }

    @RequiresApi(Build.VERSION_CODES.Q)
    private fun saveFileScopedStorage(
        displayName: String,
        content: ByteArray,
        onSuccess: (Uri) -> Unit,
        onError: (Exception) -> Unit
    ) {
        val fileType = FileType.fromFileName(displayName)
        val contentValues = ContentValues().apply {
            put(MediaStore.MediaColumns.DISPLAY_NAME, displayName)
            put(MediaStore.MediaColumns.MIME_TYPE, fileType.mimeType)
            put(MediaStore.MediaColumns.RELATIVE_PATH, fileType.directory)
        }
        val uri:Uri = when (fileType.directory) {
            Environment.DIRECTORY_PICTURES -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI
            Environment.DIRECTORY_MOVIES -> MediaStore.Video.Media.EXTERNAL_CONTENT_URI
            Environment.DIRECTORY_MUSIC -> MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
            else ->MediaStore.Files.getContentUri("external")
        }
        val insertUri: Uri? = context.contentResolver.insert(uri, contentValues)
        try {
            if (insertUri != null) {
                context.contentResolver.openOutputStream(insertUri)?.use { outputStream ->
                    outputStream.write(content)
                }
                onSuccess(insertUri)
            } else {
                onError(IOException("Failed to create new MediaStore record."))
            }
        } catch (e: Exception) {
            onError(e)
        }
    }

    private fun saveFileLegacy(
        displayName: String,
        content: ByteArray,
        onSuccess: (Uri) -> Unit,
        onError: (Exception) -> Unit
    ) {
        val fileType = FileType.fromFileName(displayName)
        val directory = File(Environment.getExternalStoragePublicDirectory(fileType.directory), displayName)
        try {
            if (!directory.parentFile.exists()) {
                directory.parentFile.mkdirs()
            }
            FileOutputStream(directory).use { outputStream ->
                outputStream.write(content)
            }
            onSuccess(Uri.fromFile(directory))
        } catch (e: Exception) {
            onError(e)
        }
    }


    fun saveFile(
        displayName: String,
        inputStream: InputStream,
        onSuccess: (Uri) -> Unit,
        onError: (Exception) -> Unit
    ) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            saveFileScopedStorage(displayName, inputStream, onSuccess, onError)
        } else {
            saveFileLegacy(displayName, inputStream, onSuccess, onError)
        }
    }

    @RequiresApi(Build.VERSION_CODES.Q)
    private fun saveFileScopedStorage(
        displayName: String,
        inputStream: InputStream,
        onSuccess: (Uri) -> Unit,
        onError: (Exception) -> Unit
    ) {
        val fileType = FileType.fromFileName(displayName)
        val contentValues = ContentValues().apply {
            put(MediaStore.MediaColumns.DISPLAY_NAME, displayName)
            put(MediaStore.MediaColumns.MIME_TYPE, fileType.mimeType)
            put(MediaStore.MediaColumns.RELATIVE_PATH, fileType.directory)
        }
        try {
            val uri:Uri = when (fileType.directory) {
                Environment.DIRECTORY_PICTURES -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI
                Environment.DIRECTORY_MOVIES -> MediaStore.Video.Media.EXTERNAL_CONTENT_URI
                Environment.DIRECTORY_MUSIC -> MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
                else ->MediaStore.Files.getContentUri("external")
            }
            val insertUri: Uri? = context.contentResolver.insert(uri, contentValues)
            if (insertUri != null) {
                context.contentResolver.openOutputStream(insertUri)?.use { outputStream ->
                    inputStream.copyTo(outputStream)
                }
                onSuccess(insertUri)
            } else {
                onError(IOException("Failed to create new MediaStore record."))
            }
        } catch (e: Exception) {
            onError(e)
        }
    }

    private fun saveFileLegacy(
        displayName: String,
        inputStream: InputStream,
        onSuccess: (Uri) -> Unit,
        onError: (Exception) -> Unit
    ) {
        val fileType = FileType.fromFileName(displayName)
        val directory = File(Environment.getExternalStoragePublicDirectory(fileType.directory), displayName)
        try {
            if (!directory.parentFile.exists()) {
                directory.parentFile.mkdirs()
            }
            FileOutputStream(directory).use { outputStream ->
                inputStream.copyTo(outputStream)
            }
            onSuccess(Uri.fromFile(directory))
        } catch (e: Exception) {
            onError(e)
        }
    }

    fun readFile(
        uri: Uri,
        onSuccess: (ByteArray) -> Unit,
        onError: (Exception) -> Unit
    ) {
        try {
            val content = context.contentResolver.openInputStream(uri)?.buffered()?.use { inputStream ->
                inputStream.readBytes()
            }
            if (content != null) {
                onSuccess(content)
            } else {
                onError(IOException("Failed to read file content."))
            }
        } catch (e: Exception) {
            onError(e)
        }
    }

    fun updateFile(
        uri: Uri,
        newContent: ByteArray,
        onSuccess: () -> Unit,
        onError: (Exception) -> Unit
    ) {
        try {
            context.contentResolver.openOutputStream(uri)?.use { outputStream ->
                outputStream.write(newContent)
                onSuccess()
            } ?: onError(IOException("Failed to open output stream."))
        } catch (e: Exception) {
            onError(e)
        }
    }

    /**
     * 删除文件
     */
    fun deleteFile(
        uri: Uri,
        onSuccess: () -> Unit,
        onError: (Exception) -> Unit
    ) {
        try {
            val rowsDeleted = context.contentResolver.delete(uri, null, null)
            if (rowsDeleted > 0) {
                onSuccess()
            } else {
                onError(IOException("Failed to delete file."))
            }
        } catch (e: Exception) {
            onError(e)
        }
    }

    /**
     * 重命名文件
     */
    fun renameFile(
        uri: Uri,
        newDisplayName: String,
        onSuccess: (Uri) -> Unit,
        onError: (Exception) -> Unit
    ) {
        val contentValues = ContentValues().apply { put(MediaStore.MediaColumns.DISPLAY_NAME, newDisplayName) }
        try {
            val rowsUpdated = context.contentResolver.update(uri, contentValues, null, null)
            if (rowsUpdated > 0) {
                onSuccess(uri)
            } else {
                onError(IOException("Failed to rename file."))
            }
        } catch (e: Exception) {
            onError(e)
        }
    }


    /**
     * 读取文件
     */
    fun readFile(
        displayName: String,
        onSuccess: (ByteArray) -> Unit,
        onError: (Exception) -> Unit
    ) {
//        val fileType = FileType.fromFileName(displayName)
        val selection = "${MediaStore.MediaColumns.DISPLAY_NAME}=?"
        val selectionArgs = arrayOf(displayName)
        val uri = MediaStore.Files.getContentUri("external")
        context.contentResolver.query(uri, null, selection, selectionArgs, null)?.use { cursor ->
            if (cursor.moveToFirst()) {
                val uriIndex = cursor.getColumnIndex(MediaStore.MediaColumns._ID)
                val id = cursor.getLong(uriIndex)
                val contentUri = ContentUris.withAppendedId(uri, id)
                try {
                    val content = context.contentResolver.openInputStream(contentUri)?.buffered()?.use { inputStream ->
                        inputStream.readBytes()
                    }
                    if (content != null) {
                        onSuccess(content)
                    } else {
                        onError(IOException("Failed to read file content."))
                    }
                } catch (e: Exception) {
                    onError(e)
                }
            } else {
                onError(IOException("File not found."))
            }
        } ?: onError(IOException("Failed to query MediaStore."))
    }

    fun updateFile(
        displayName: String,
        newContent: ByteArray,
        onSuccess: () -> Unit,
        onError: (Exception) -> Unit
    ) {
        val fileType = FileType.fromFileName(displayName)
        val selection = "${MediaStore.MediaColumns.DISPLAY_NAME}=?"
        val selectionArgs = arrayOf(displayName)
        val uri = MediaStore.Files.getContentUri("external")

        context.contentResolver.query(uri, null, selection, selectionArgs, null)?.use { cursor ->
            if (cursor.moveToFirst()) {
                val uriIndex = cursor.getColumnIndex(MediaStore.MediaColumns._ID)
                val id = cursor.getLong(uriIndex)
                val contentUri = ContentUris.withAppendedId(uri, id)
                try {
                    context.contentResolver.openOutputStream(contentUri)?.use { outputStream ->
                        outputStream.write(newContent)
                        onSuccess()
                    } ?: onError(IOException("Failed to open output stream."))
                } catch (e: Exception) {
                    onError(e)
                }
            } else {
                onError(IOException("File not found."))
            }
        } ?: onError(IOException("Failed to query MediaStore."))
    }

    fun deleteFileByName(
    displayName: String,
    onSuccess: () -> Unit,
    onError: (Exception) -> Unit
    ) {
        val selection = "${MediaStore.MediaColumns.DISPLAY_NAME}=?"
        val selectionArgs = arrayOf(displayName)
        val fileType = FileType.fromFileName(displayName)
        val uri:Uri = when (fileType.directory) {
            Environment.DIRECTORY_PICTURES -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI
            Environment.DIRECTORY_MOVIES -> MediaStore.Video.Media.EXTERNAL_CONTENT_URI
            Environment.DIRECTORY_MUSIC -> MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
            else ->MediaStore.Files.getContentUri("external")
        }
        context.contentResolver.query(uri, null, selection, selectionArgs, null)?.use { cursor ->
            if (cursor.moveToFirst()) {
                val uriIndex = cursor.getColumnIndex(MediaStore.MediaColumns._ID)
                val id = cursor.getLong(uriIndex)
                val contentUri = ContentUris.withAppendedId(uri, id)
                try {
                    val rowsDeleted = context.contentResolver.delete(contentUri, null, null)
                    if (rowsDeleted > 0) {
                        onSuccess()
                    } else {
                        onError(IOException("Failed to delete file."))
                    }
                } catch (e: Exception) {
                    onError(e)
                }
            } else {
                onError(IOException("File not found."))
            }
        } ?: onError(IOException("Failed to query MediaStore."))
    }

    fun queryFileByName(
    displayName: String,
    onSuccess: (Uri?) -> Unit,
    ) {
        val selection = "${MediaStore.MediaColumns.DISPLAY_NAME}=?"
        val selectionArgs = arrayOf(displayName)
        val fileType = FileType.fromFileName(displayName)
        val uri:Uri = when (fileType.directory) {
            Environment.DIRECTORY_PICTURES -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI
            Environment.DIRECTORY_MOVIES -> MediaStore.Video.Media.EXTERNAL_CONTENT_URI
            Environment.DIRECTORY_MUSIC -> MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
            else ->MediaStore.Files.getContentUri("external")
        }
        context.contentResolver.query(uri, null, selection, selectionArgs, null)?.use { cursor ->
            if (cursor.moveToFirst()) {
                val uriIndex = cursor.getColumnIndex(MediaStore.MediaColumns._ID)
                val id = cursor.getLong(uriIndex)
                val contentUri = ContentUris.withAppendedId(uri, id)
                onSuccess.invoke(contentUri)
            } else {
                onSuccess.invoke(null)
                Log.e("StorageRepository","File not found.")
            }
        } ?: {
            onSuccess.invoke(null)
            Log.e("StorageRepository","Failed to query MediaStore.")
        }
    }

    fun writeInputStreamToAlbum(inputStream: InputStream, displayName: String, mimeType: String) {
        val values = ContentValues()
        values.put(MediaStore.MediaColumns.DISPLAY_NAME, displayName)
        values.put(MediaStore.MediaColumns.MIME_TYPE, mimeType)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM)
        } else {
            values.put(MediaStore.MediaColumns.DATA, "${Environment.getExternalStorageDirectory().path}/${Environment.DIRECTORY_DCIM}/$displayName")
        }
        BufferedInputStream(inputStream)?.use {bis->
            val uri = context.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
            if (uri != null) {
                context.contentResolver.openOutputStream(uri)?.use {outputStream->
                    val bos = BufferedOutputStream(outputStream)
                    val buffer = ByteArray(1024)
                    var bytes = bis.read(buffer)
                    while (bytes >= 0) {
                        bos.write(buffer, 0 , bytes)
                        bos.flush()
                        bytes = bis.read(buffer)
                    }
                }
            }
        }
    }
//    /**
//     * 保存图片并展示到相册
//     * @param displayName 图片名称 包含后缀
//     * @param bitmap 图片
//     */
//    fun addBitmapToAlbum(
//        displayName: String,
//        bitmap: Bitmap,
//        onSuccess: (Uri) -> Unit,
//        onError: (Exception) -> Unit
//    ) {
//        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
//            addBitmapToAlbumScopedStorage(displayName, bitmap, onSuccess, onError)
//        } else {
//            addBitmapToAlbumLegacy(displayName, bitmap, onSuccess, onError)
//        }
//    }
//
//    @RequiresApi(Build.VERSION_CODES.Q)
//    private fun addBitmapToAlbumScopedStorage(
//        displayName: String,
//        bitmap: Bitmap,
//        onSuccess: (Uri) -> Unit,
//        onError: (Exception) -> Unit
//    ) {
//        val fileType = FileType.fromFileName(displayName)
//        val contentValues = ContentValues().apply {
//            put(MediaStore.MediaColumns.DISPLAY_NAME, displayName)
//            put(MediaStore.MediaColumns.MIME_TYPE, fileType.mimeType)
//            put(MediaStore.MediaColumns.RELATIVE_PATH, fileType.directory)
//        }
//
//        val byteArrayOutputStream = ByteArrayOutputStream().apply {
//            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, this)
//        }
//        val byteArray = byteArrayOutputStream.toByteArray()
//        try {
//            val uri = context.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
//            if (uri != null) {
//                context.contentResolver.openOutputStream(uri)?.use { outputStream ->
//                    outputStream.write(byteArray)
//                }
//                onSuccess(uri)
//            } else {
//                onError(IOException("Failed to create new MediaStore record."))
//            }
//        } catch (e: Exception) {
//            onError(e)
//        }
//    }
//
//    private fun addBitmapToAlbumLegacy(
//        displayName: String,
//        bitmap: Bitmap,
//        onSuccess: (Uri) -> Unit,
//        onError: (Exception) -> Unit
//    ) {
//        val fileType = FileType.fromFileName(displayName)
//        val directory = File(Environment.getExternalStoragePublicDirectory(fileType.directory), displayName)
//        try {
//            if (!directory.parentFile.exists()) {
//                directory.parentFile.mkdirs()
//            }
//            FileOutputStream(directory).use { outputStream ->
//                bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
//            }
//            onSuccess(Uri.fromFile(directory))
//        } catch (e: Exception) {
//            onError(e)
//        }
//    }


    /**
     * 保存图片并展示到相册
     * @param displayName 图片名称 包含后缀
     * @param bitmap 图片
     */
    fun addBitmapToAlbum(
        displayName: String,
        bitmap: Bitmap,
        onSuccess: (Uri) -> Unit,
        onError: (Exception) -> Unit
    ) {
        val fileType = FileType.fromFileName(displayName)
        val content = bitmapToByteArray(bitmap, fileType)
        saveFile(displayName, content, onSuccess, onError)
    }

    private fun bitmapToByteArray(bitmap: Bitmap, fileType: FileType): ByteArray {
        val format = when (fileType) {
            FileType.IMAGE_PNG -> Bitmap.CompressFormat.PNG
            else -> Bitmap.CompressFormat.JPEG
        }
        return ByteArrayOutputStream().apply {
            bitmap.compress(format, 100, this)
        }.toByteArray()
    }
}